package cn.kivensoft.util;

import java.util.function.IntConsumer;
import java.util.function.IntPredicate;

/** 二进制与十六进制之间的转换及十六进制字符串对某个位的测试辅助工具类
 * @author kiven lee
 * @version 1.1
 */
final public class Bits {
	private Bits() {}

	/** 获取16进制字符串中指定位置的0/1标志
	 * @param hex 16进制表示的字符串
	 * @param index 索引位
	 * @return true: 该位为1, false: 该位为0
	 */
	public static boolean getBit(String hex, int index) {
		if (hex == null || index < 0 || index >= (hex.length() << 2))
			return false;
		byte b = c2b(hex.charAt(index >> 2));
		return (b & (8 >> (index & 3))) != 0;
	}

	/** 设置16进制字符串中指定位置的0/1标志
	 *
	 * @param hex 16进制字符串
	 * @param index 索引位
	 * @param value 新的值
	 * @return 新的16进制字符串
	 */
	public static String setBit(String hex, int index, boolean value) {
		if (index < 0) return "";

		// 计算新字符串长度
		int newLen = ((index + 8) >> 3) << 1;
		if (hex != null && newLen < hex.length())
			newLen = hex.length();

		// 用原有的内容+扩展’0‘填充
		StringBuilder sb = new StringBuilder(newLen);
		int pos = 0;
		if (hex != null) {
			sb.append(hex);
			pos = hex.length();
		}
		for (; pos < newLen; ++pos)
			sb.append('0');

		// 设置对应的位标志
		pos = index >> 2;
		byte b = c2b(sb.charAt(pos));
		byte n = (byte) (8 >> (index & 3));
		if (value) b |= n;
		else b &= (byte) ~n;
		sb.setCharAt(pos, (char) (b < 10 ?  48 + b : 87 + b));

		return sb.toString();
	}

	/** 将boolean数组转成16进制字符串表示形式
	 * @param booleans boolean数组
	 * @return 16进制的字符串表示形式
	 */
	public static String toHexString(boolean[] booleans) {
		return booleans == null ? "" : toHexString(booleans.length, i -> booleans[i]);
	}

	/** 将count数量的boolean转换为16进制文本表示
	 * @param count 转换数量
	 * @param pred 回调接口, 参数为(index), 根据回调结果设置对应位
	 * @return 16进制的字符串表示形式
	 */
	public static String toHexString(int count, IntPredicate pred) {
		if (count == 0) return "";

		int blen = (count + 7) >> 3;
		char[] chars = new char[blen << 1];
		// 8倍数的位判断, 写入数组
		for (int i = 0, imax = count >> 3; i < imax; ++i) {
			byte b = 0;
			int index = i << 3;
			for (int j = 0; j < 8; ++j)
				if (pred.test(index + j)) b |= (byte) (0x80 >> j);
			int b1 = b >> 4 & 0xF, b2 = b & 0xF;
			chars[i << 1] = (char) (b1 < 10 ?  48 + b1 : 87 + b1);
			chars[(i << 1) + 1] = (char) (b2 < 10 ? 48 + b2 : 87 + b2);
		}

		// 剩余位数, 如果有, 则写入最后一个字节
		int surplus = count & 7;
		if (surplus > 0) {
			byte b = 0;
			int index = (blen - 1) << 3;
			for (int j = 0; j < surplus; ++j)
				if (pred.test(index + j)) b |= (byte) (0x80 >> j);
			int b1 = b >> 4 & 0xF, b2 = b & 0xF;
			chars[(blen - 1) << 1] = (char) (b1 < 10 ?  48 + b1 : 87 + b1);
			chars[((blen - 1) << 1) + 1] = (char) (b2 < 10 ? 48 + b2 : 87 + b2);
		}

		// 从尾部开始查找为0的数据, 尾部为0的截断
		for (int i = chars.length - 1; i > 0; i -= 2) {
			if (chars[i] != '0' || chars[i - 1] != '0') {
				return new String(chars, 0, i + 1);
			}
		}

		return "";
	}

	/** 将16进制字符串转换为boolean数组
	 * @param hex 16进制字符串
	 * @return boolean数组
	 */
	public static boolean[] parseHexString(String hex) {
		int len = hex == null ? 0 : hex.length();
		boolean[] ret = new boolean[len << 2];
		parseHexString(hex, index -> ret[index] = true);
		return ret;
	}

	/** 解析16进制字符串, 对每个为1的位进行回调处理, 该位如果为0, 不进行回调
	 * @param hex 16进制字符串
	 * @param func 回调接口
	 */
	public static void parseHexString(String hex, IntConsumer func) {
		if (hex == null || hex.isEmpty()) return;
		for (int i = 0, imax = hex.length(); i < imax; ++i) {
			int index = i << 2;
			byte b = c2b(hex.charAt(i));
			if ((b & 8) != 0) func.accept(index);
			if ((b & 4) != 0) func.accept(index + 1);
			if ((b & 2) != 0) func.accept(index + 2);
			if ((b & 1) != 0) func.accept(index + 3);
		}
	}

	// 16进制表示的字符转成二进制表示
	private static  byte c2b(char c) {
		if ((c & 64) != 0) c += 9;
		return (byte) (c & 15);
	}

}
